#import "FistLocalAuthPlugin.h"
#import <LocalAuthentication/LocalAuthentication.h>


@implementation FistLocalAuthPlugin {
    NSDictionary *lastCallArgs;
    FlutterResult lastResult;
}
+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar> *)registrar {
    FlutterMethodChannel *channel =
    [FlutterMethodChannel methodChannelWithName:@"auth.plugins.flutter.io/fist_local_auth"
                                binaryMessenger:[registrar messenger]];
    FistLocalAuthPlugin *instance = [[FistLocalAuthPlugin alloc] init];
    [registrar addMethodCallDelegate:instance channel:channel];
    [registrar addApplicationDelegate:instance];
}

- (void)handleMethodCall:(FlutterMethodCall *)call result:(FlutterResult)result {
    if ([@"authenticateWithBiometrics" isEqualToString:call.method]) {
        [self authenticateWithBiometrics:call.arguments withFlutterResult:result];
    } else if ([@"getAvailableBiometrics" isEqualToString:call.method]) {
        [self getAvailableBiometrics:result];
    }else if ([@"touchIdInfoDidChange" isEqualToString:call.method]) {
        [self touchIdInfoDidChange:result];
    }else if ([@"clearKeys" isEqualToString:call.method]) {
        [self clearKeys];
    } else {
        result(FlutterMethodNotImplemented);
    }
}

#pragma mark Private Methods

- (void)alertMessage:(NSString *)message
         firstButton:(NSString *)firstButton
       flutterResult:(FlutterResult)result
    additionalButton:(NSString *)secondButton {
    UIAlertController *alert =
    [UIAlertController alertControllerWithTitle:@""
                                        message:message
                                 preferredStyle:UIAlertControllerStyleAlert];
    
    UIAlertAction *defaultAction = [UIAlertAction actionWithTitle:firstButton
                                                            style:UIAlertActionStyleDefault
                                                          handler:^(UIAlertAction *action) {
                                                              result(@NO);
                                                          }];
    
    [alert addAction:defaultAction];
    if (secondButton != nil) {
        UIAlertAction *additionalAction = [UIAlertAction
                                           actionWithTitle:secondButton
                                           style:UIAlertActionStyleDefault
                                           handler:^(UIAlertAction *action) {
                                               if (UIApplicationOpenSettingsURLString != NULL) {
                                                   NSURL *url = [NSURL URLWithString:UIApplicationOpenSettingsURLString];
                                                   [[UIApplication sharedApplication] openURL:url];
                                                   result(@NO);
                                               }
                                           }];
        [alert addAction:additionalAction];
    }
    [[UIApplication sharedApplication].delegate.window.rootViewController presentViewController:alert
                                                                                       animated:YES
                                                                                     completion:nil];
}

- (void)getAvailableBiometrics:(FlutterResult)result {
    LAContext *context = [[LAContext alloc] init];
    NSError *authError = nil;
    NSMutableArray<NSString *> *biometrics = [[NSMutableArray<NSString *> alloc] init];
    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                             error:&authError]) {
        if (authError == nil) {
            if (@available(iOS 11.0.1, *)) {
                if (context.biometryType == LABiometryTypeFaceID) {
                    [biometrics addObject:@"face"];
                } else if (context.biometryType == LABiometryTypeTouchID) {
                    [biometrics addObject:@"fingerprint"];
                }
            } else {
                [biometrics addObject:@"fingerprint"];
            }
        }
    } else if (authError.code == LAErrorTouchIDNotEnrolled) {
        [biometrics addObject:@"undefined"];
    }
    result(biometrics);
}

- (void)authenticateWithBiometrics:(NSDictionary *)arguments
                 withFlutterResult:(FlutterResult)result {
    LAContext *context = [[LAContext alloc] init];
    NSError *authError = nil;
    lastCallArgs = nil;
    lastResult = nil;
    context.localizedFallbackTitle = @"";
    
    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                             error:&authError]) {
       if ([self isChanged:context]) {
            result([FlutterError errorWithCode:@"changed"
                                       message:nil
                                       details:nil]);
            return;
        }
       
        [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                localizedReason:arguments[@"localizedReason"]
                          reply:^(BOOL success, NSError *error) {
                              if (success) {
                                  if (@available(iOS 9.0, *)) {
                                      NSData *data = [context evaluatedPolicyDomainState];
                                      NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
                                      [defaults setObject:data forKey:@"evaluatedPolicyDomainState"];
                                      [defaults synchronize];
                                  } else {
                                      // Fallback on earlier versions
                                  }

                                  result(@YES);
                              } else {
                                  switch (error.code) {
                                      case LAErrorPasscodeNotSet:
                                      case LAErrorTouchIDNotAvailable:
                                      case LAErrorTouchIDNotEnrolled:
                                      case LAErrorTouchIDLockout:
                                          [self handleErrors:error
                                            flutterArguments:arguments
                                           withFlutterResult:result];
                                          return;
                                      case LAErrorSystemCancel:
                                          if ([arguments[@"stickyAuth"] boolValue]) {
                                              lastCallArgs = arguments;
                                              lastResult = result;
                                              return;
                                          }
                                  }
                                  result(@NO);
                              }
                          }];
    } else {
        [self handleErrors:authError flutterArguments:arguments withFlutterResult:result];
    }
}

- (void)handleErrors:(NSError *)authError
    flutterArguments:(NSDictionary *)arguments
   withFlutterResult:(FlutterResult)result {
    NSString *errorCode = @"NotAvailable";
    switch (authError.code) {
        case LAErrorPasscodeNotSet:
        case LAErrorTouchIDNotEnrolled:
            if ([arguments[@"useErrorDialogs"] boolValue]) {
                [self alertMessage:arguments[@"goToSettingDescriptionIOS"]
                       firstButton:arguments[@"okButton"]
                     flutterResult:result
                  additionalButton:arguments[@"goToSetting"]];
                return;
            }
            errorCode = authError.code == LAErrorPasscodeNotSet ? @"PasscodeNotSet" : @"NotEnrolled";
            break;
        case LAErrorTouchIDLockout:
            [self alertMessage:arguments[@"lockOut"]
                   firstButton:arguments[@"okButton"]
                 flutterResult:result
              additionalButton:nil];
            return;
    }
    result([FlutterError errorWithCode:errorCode
                               message:authError.localizedDescription
                               details:authError.domain]);
}


- (void)clearKeys{
    
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    
    [userDefaults removeObjectForKey:@"evaluatedPolicyDomainState"];
    [userDefaults synchronize];
    
}

- (void)touchIdInfoDidChange:(FlutterResult)result{
    
    if ([self isChanged:[[LAContext alloc] init]]) {
        result(@YES);
    }else{
        result(@NO);
    }

}

- (BOOL)isChanged:(LAContext *)context{
    NSError *authError = nil;
    if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics
                             error:&authError]) {
        if (@available(iOS 9.0, *)) {
            NSData *data = [context evaluatedPolicyDomainState];
           NSData *old_data = [[NSUserDefaults standardUserDefaults] objectForKey:@"evaluatedPolicyDomainState"];

            if (![data isEqualToData:old_data] && old_data) {
                return YES;
            }
        } else {
            // Fallback on earlier versions
            return NO;
        }
    }
    return NO;
}

#pragma mark - AppDelegate

- (void)applicationDidBecomeActive:(UIApplication *)application {
    if (lastCallArgs != nil && lastResult != nil) {
        [self authenticateWithBiometrics:lastCallArgs withFlutterResult:lastResult];
    }
}


@end

